<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
    <title>实景飞行</title>
    <script src="/Cesium.js"></script>
    <style>
        @import url(/Widgets/widgets.css);
        html,
        body,
        #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        
        .btn-container {
            position: absolute;
            left: 10px;
            top: 90px;
            padding: 10px 15px;
            border-radius: 4px;
            border: 1px solid rgba(128, 128, 128, 0.5);
            color: #ffffff;
            background: rgba(0, 0, 0, 0.4);
            box-shadow: 0 3px 14px rgb(128 128 128 / 50%);
            max-width: 300px;
        }
        
        button {
            background: transparent;
            border: 1px solid #00d0ffb8;
            color: white;
            padding: 7px 9px;
            border-radius: 2px;
            margin: 3px;
            cursor: pointer
        }
        
        .tip-item {
            margin: 2px 0px;
            padding: 5px 1px;
        }
        
        .cesium-widget-credits {
            display: none !important;
        }
    </style>
</head>

<body>
    <div id="cesiumContainer"></div>
    <div class="btn-container">
        <button onclick="mysettime()">飞行</button>
        <button onclick="">暂停</button>
        <button onclick="">开始</button>
        <button onclick="mysoptime()">停止</button>

    </div>
    <script>
        var key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2MzJjMWZjNi02MmVkLTRkYmUtOGU2ZC0wZDY2ZTAxMzRkMjgiLCJpZCI6MzIxMTYsInNjb3BlcyI6WyJhc2wiLCJhc3IiLCJhc3ciLCJnYyIsInByIl0sImlhdCI6MTU5NjUwOTQ2MX0.gloYT1aON7-sqIUpS82z62k1uE49kfYLmvRon1oxSIM"
        Cesium.Ion.defaultAccessToken = key;

        var viewer = new Cesium.Viewer('cesiumContainer', {
            geocoder: true, //是否显示geocoder小器件，右上角查询按钮
            homeButton: true, //是否显示Home按钮
            sceneModePicker: true, //是否显示3D/2D选择器
            baseLayerPicker: true, //是否显示图层选择器
            navigationHelpButton: true, //是否显示右上角的帮助按钮
            animation: true, //是否创建动画小器件，左下角仪表
            creditContainer: "cesiumContainer", // 对应上面div的ID
            timeline: true, //是否显示时间轴
            fullscreenButtion: true, //是否显示全屏按钮
            vrButton: true, //是否显示VR按钮
            selectionIndicator: false, //是否显示选取指示器组件
            imageryLayers: new Cesium.UrlTemplateImageryProvider({
                url: 'http://mt0.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}',
                tilingScheme: new Cesium.WebMercatorTilingScheme(),
                maximumLevel: 20
            })
        });
        viewer.scene.debugShowFramesPerSecond = true; // 显示帧率

        // 地标集合 根据地标顺序来进行漫游
        var marks = [{
                lng: 109.70065490293072,
                lat: 39.58077810030326,
                height: 800,
                flytime: 7
            }, // height:相机高度(单位米) flytime:相机两个标注点飞行时间(单位秒)
            {
                lng: 109.74636979454736,
                lat: 39.5775752533593,
                height: 750,
                flytime: 7
            }, {
                lng: 109.76360608903445,
                lat: 39.58348666794168,
                height: 700,
                flytime: 7
            }, {
                lng: 109.78338892551875,
                lat: 39.58107006213701,
                height: 600,
                flytime: 7
            }, {
                lng: 109.79183442169095,
                lat: 39.58223301767059,
                height: 500,
                flytime: 7
            }, {
                lng: 109.77540624743895,
                lat: 39.60962910263723,
                height: 200,
                flytime: 15
            }
        ];

        var marksIndex = 1; //默认飞行位置下标
        var pitchValue = -20; // 相机看点的角度，如果大于0那么则是从地底往上看，所以要为负值
        var meter; //计时器
        var Exection; //全局时钟事件

        function mysettime() {
            //系统初始化定位到第一个点位
            viewer.scene.camera.flyTo({
                destination: Cesium.Cartesian3.fromDegrees(marks[0].lng, marks[0].lat, marks[0].height),
                duration: 6 //定位的时间间隔
            });
            //延时执行相机飞行操作
            meter = setTimeout(function() {
                flyExtent();
            }, 6500);
        };

        function mysoptime() {
            viewer.clock.onTick.removeEventListener(Exection);
        };

        /*
         * 飞行执行函数
         */
        function flyExtent() {
            var pitch = Cesium.Math.toRadians(pitchValue);
            // 设置时钟
            setExtentTime(marks[marksIndex].flytime);
            Exection = function TimeExecution() {
                var preIndex = marksIndex - 1;
                if (marksIndex == 0) {
                    preIndex = marks.length - 1;
                }
                var heading = bearing(marks[preIndex].lat, marks[preIndex].lng, marks[marksIndex].lat, marks[marksIndex].lng);
                heading = Cesium.Math.toRadians(heading);
                // 当前已经过去的时间，单位s
                var delTime = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);
                var originLat = marksIndex == 0 ? marks[marks.length - 1].lat : marks[marksIndex - 1].lat;
                var originLng = marksIndex == 0 ? marks[marks.length - 1].lng : marks[marksIndex - 1].lng;
                var endPosition = Cesium.Cartesian3.fromDegrees(
                    (originLng + (marks[marksIndex].lng - originLng) / marks[marksIndex].flytime * delTime),
                    (originLat + (marks[marksIndex].lat - originLat) / marks[marksIndex].flytime * delTime),
                    marks[marksIndex].height
                );
                viewer.scene.camera.setView({
                    destination: endPosition,
                    orientation: {
                        heading: heading,
                        pitch: pitch,
                    }
                });
                //若预设该飞行时间已过期，则清空监听事件并原地转向
                if (Cesium.JulianDate.compare(viewer.clock.currentTime, viewer.clock.stopTime) >= 0) {
                    viewer.clock.onTick.removeEventListener(Exection);
                    changeCameraHeading();
                }
            };
            viewer.clock.onTick.addEventListener(Exection);
        }

        // 相机原地定点转向
        function changeCameraHeading() {
            var nextIndex = marksIndex + 1;
            if (marksIndex == marks.length - 1) {
                nextIndex = 0;
            }
            // 计算两点之间的方向
            var heading = bearing(marks[marksIndex].lat, marks[marksIndex].lng, marks[nextIndex].lat, marks[nextIndex].lng);
            // 相机看点的角度，如果大于0那么则是从地底往上看，所以要为负值
            var pitch = Cesium.Math.toRadians(pitchValue);
            // 给定飞行一周所需时间，比如10s, 那么每秒转动度数
            var angle = (heading - Cesium.Math.toDegrees(viewer.camera.heading)) / 2;
            // 时间间隔2秒钟
            setExtentTime(2);
            // 相机的当前方向
            var initialHeading = viewer.camera.heading;
            Exection = function() {
                // 当前已经过去的时间，单位s
                var delTime = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);
                var heading = Cesium.Math.toRadians(delTime * angle) + initialHeading;
                viewer.scene.camera.setView({
                    orientation: {
                        heading: heading,
                        pitch: pitch,
                    }
                });
                //相机原地转完执行飞往下一点事件
                if (Cesium.JulianDate.compare(viewer.clock.currentTime, viewer.clock.stopTime) >= 0) {
                    viewer.clock.onTick.removeEventListener(Exection);
                    marksIndex = ++marksIndex >= marks.length ? 0 : marksIndex;
                    flyExtent();
                }
            };
            viewer.clock.onTick.addEventListener(Exection);
        }

        // 设置飞行的时间到viewer的时钟里
        function setExtentTime(time) {
            var startTime = Cesium.JulianDate.fromDate(new Date());
            var stopTime = Cesium.JulianDate.addSeconds(startTime, time, new Cesium.JulianDate());
            viewer.clock.startTime = startTime.clone(); // 开始时间
            viewer.clock.stopTime = stopTime.clone(); // 结束时间
            viewer.clock.currentTime = startTime.clone(); // 当前时间
            viewer.clock.clockRange = Cesium.ClockRange.CLAMPED; // 行为方式：到时间后时钟将停止
            viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK; // 时钟设置为当前系统时间; 忽略所有其他设置。
        }

        /** 相机视角飞行 结束 **/
        // 弧度转角度
        function toRadians(degrees) {
            return degrees * Math.PI / 180;
        }

        //角度转弧度
        function toDegrees(radians) {
            return radians * 180 / Math.PI;
        }

        /*
         * 计算两点间角度
         *   startLat：起点纬度
         *   startLng：起点经度
         *   destLat：终点纬度
         *   destLng：终点经度
         * Return 对于正北角度
         */
        function bearing(startLat, startLng, destLat, destLng) {
            startLat = this.toRadians(startLat);
            startLng = this.toRadians(startLng);
            destLat = this.toRadians(destLat);
            destLng = this.toRadians(destLng);

            let y = Math.sin(destLng - startLng) * Math.cos(destLat);
            let x = Math.cos(startLat) * Math.sin(destLat) - Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
            let brng = Math.atan2(y, x);
            let brngDgr = this.toDegrees(brng);
            return (brngDgr + 360) % 360;
        }
    </script>
</body>

</html>